package com.zwh.se.oop.basic;

import com.zwh.se.common.pojo.POJO;

/**
 * 说明一下Java的参数传递的唯一方式：值传递(传值)（By Value）
 * <p>
 * 实际上，值传递和引用传递是一种其他语言中才存在的概念，因此，这个解释本质上是为了向其他语言程序员解释而特地说明的
 *
 * @author yexi
 */
public class ByValue {
    public static void main(String[] args) {

        //从下面的例子其实就能够很明显地看出来了(就不做基本数据类型的解说了)
        POJO pojo = new POJO(2L, "小红", 15);
        changeObject(pojo);
        System.out.println("调用完方法之后的原对象：\t" + pojo);
        //这就是值传递，也就是当传递对象时，实际上只是传递了一个地址值，而方法内部调用的new，只不过是将其本来存储的地址值换了一个而已，因此并不会影响到原对象。

        System.out.println("----------------------");
        //重点来了!根据上面的说法，传递的是对象的地址，那么如果我并不是像上面这个方法那样是写个new，而是对里面的值进行了修改呢？
        changeObject1(pojo);
        System.out.println("调用完方法之后的原对象：\t" + pojo);
        //可以明显地看到原对象的属性被彻底地变更了，无论它的属性是引用类型亦或者是基础类型也罢

        // 那么就再来一个更为准确的对"值传递"这个词的解释：
        // 当传递实际参数时，传递的是实际参数本身的那个值，随后通过一个新声明的形式参数接收了这个值，而形式参数对值的操作不会影响到原实体参数
        // 当传递对象时，就应该理解为传递的是对象的引用这个值(在其他语言中存在这个概念,也就是我们常说的地址)，但这个值并不是对象本身，而是指向该对象，
        //    因此，如果你对形式参数采取了new操作，代表着的是该形式参数将其存储的对象引用值变为了一个新的引用。所以不会影响到原对象。
        //    而如果你选择直接通过对象本身的方法来对其操作的话，那么就是通过该引用直接影响到了原对象。

        System.out.println("----------带入一些内存分析的解释------------");
        //为了更好地理解，此处还是引用一些内存分析来更好地理解。
        int i = 10, j = 20;
        change(i, j);
        System.out.println("i=" + i + " j=" + j);
        // 那么现在开始分析：
        // 首先这里是一个main方法，于是在main方法执行时，会将一个main对应的"栈帧"压到栈区当中，而"栈区"内部存在着局部变量表，
        //   在这个局部变量表中就存储有i和j这两个变量(之前上面那些就忽略不计了)
        // 然后现在调用了change方法，并将i和j的值传递了过去，于是change方法执行，又将一个change对应的"栈帧"压到栈区当中，
        //   而change的局部变量表中也存了两个叫做i和j的变量，并且通过赋值，它的i=10，j=20，在赋值结束之后，change的"栈帧"销毁
        //   于是，在change方法里面对变量的变更只会影响到change方法本身的局部变量表罢了，它不会影响到main

        // 然后再分析一下之前的对象传递：
        // 同样的道理，main方法压栈，而局部变量表里面就存储着pojo这个变量，但是注意了，pojo是个引用数据类型，因此它本身是不存储属性值的。
        // 它只会去堆区开辟出一片空间，然后值的操作都是在堆区中进行操作的，而它自己，所存储的只是一个可以指向堆区中那些对应变量的地址(或者叫做引用)罢了
        // 因此，当changeObject接收到传递过来的值的时候，传递的正是这个引用。
        // 也正因此，想changeObject1这样通过方法来对引用中的变量进行操作，就是会影响到原对象。
        //  而如果存储在changeObject局部变量表中的pojo用了new的时候，由于new的时候会重新开辟一份新的堆空间然后返回来存储，因此，在这么做的瞬间，
        //  changeObject中的pojo就存储了另外一个与原对象所不同的引用，因此这时无论对changeObject中的pojo怎么操作都不会影响到原对象了。


        // 结论：Java中一切都是值传递。
        // 当使用方法的时候，实参传递给形参：如果是基本数据类型，那么传递的就是字面值。如果是引用数据类型，那么传递的就是地址值。
    }

    private static void change(int i, int j) {
        i = 20;
        j = 10;
    }

    public static void changeObject(POJO pojo) {
        System.out.println("从外面刚传进来的：\t" + pojo);
        pojo = new POJO(3L, "小明", 45);
        // 这个 pojo 和外面传进来的那个 pojo 并不是同一个对象
        System.out.println("通过new重新赋值的：\t" + pojo);
    }

    public static void changeObject1(POJO pojo) {
        System.out.println("从外面刚传进来的：\t" + pojo);
        pojo.setId(3L);
        pojo.setName("小明");
        pojo.setAge(45);
        System.out.println("通过set方法重新赋值的：\t" + pojo);
    }


}
